﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PropertyExpression.ControllableQuery
{
    public interface IQuery
    {
        string TransformText();
    }

    public interface IQuery<out T> : IQuery
    {
    }

    public interface INonQuery : IQuery
    {
    }

    public class Q
    {
        public readonly StringBuilder Builder;

        public Q(string value)
        {
            Builder = new StringBuilder(value);
        }

        public Q()
            : this(string.Empty)
        {
        }

        public Q _(string value)
        {
            Builder.Append(value);
            return this;
        }

        public Q _<T>(IParam<T> param)
        {
            return _(param.ToSql());
        }

        public override string ToString()
        {
            return Builder.ToString();
        }
    }

    public static class QueryExtensions
    {
        public static IQuery<T> Select<T>(this Q it, Func<ColumnSelector, T> columnSelectorFunc)
        {
            if (!DynamicMaterializer.IsAnonymousType(typeof (T))) throw new NotSupportedException();

            var columnSelector = new ColumnSelector();
            columnSelectorFunc(columnSelector);

            var parameterNames = Cache<T>.ConstructorParameterNames;
            var builder = new StringBuilder();
            for (var index = 0; index < parameterNames.Count; index++)
            {
                if (builder.Length != 0) builder.Append(@",
        ");
                var parameterName = parameterNames[index];
                columnSelector.ColumnRefs[index].Process(
                    _ => builder.Append(_ + parameterName),
                    () => builder.Append(parameterName));
            }
            if (it.Builder[0] != '\r') builder.AppendLine();
            return new QueryImpl<T>(builder.Insert(0, @"SELECT  ").Append(it.ToString()).ToString());
        }

        private static class Cache<T>
        {
            public static readonly List<string> ConstructorParameterNames =
                typeof (T).GetConstructors()[0].GetParameters().Select(_ => _.Name).ToList();
        }

        public static IQuery<T> Query<T>(this Q q, T recordExample)
        {
            return new QueryImpl<T>(q.ToString());
        }

        public static IQuery<T> Query<T>(this Q q)
        {
            return new QueryImpl<T>(q.ToString());
        }

        private class QueryImpl<T> : IQuery<T>
        {
            private readonly string queryText;

            public QueryImpl(string queryText)
            {
                this.queryText = queryText;
            }

            public string TransformText()
            {
                return queryText;
            }
        }

        public static Type QueryImplementationType
        {
            get { return typeof (QueryImpl<>); }
        }

        public static INonQuery NonQuery(this Q q)
        {
            return new NonQueryImpl(q.ToString());
        }

        private class NonQueryImpl : INonQuery
        {
            private readonly string queryText;

            public NonQueryImpl(string queryText)
            {
                this.queryText = queryText;
            }

            public string TransformText()
            {
                return queryText;
            }
        }

        public static Type NonQueryImplementationType
        {
            get { return typeof(NonQueryImpl); }
        }
    }
}